package com.jzy.dl.common.utils;


/**
 * Twitter_Snowflake<br>
 * SnowFlake的结构如下(每部分用-分开):<br>
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
 * 1位标识，由于long基本类型在Java中是带符号的，最高位是符号位，正数是0，负数是1，所以id一般是正数，最高位是0<br>
 * 41位时间截(毫秒级)，注意，41位时间截不是存储当前时间的时间截，而是存储时间截的差值（当前时间截 - 开始时间截)
 * 得到的值），这里的的开始时间截，一般是我们的id生成器开始使用的时间，由我们程序来指定的（如下下面程序IdWorker类的startTime属性）。41位的时间截，可以使用69年，年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
 * 10位的数据机器位，可以部署在1024个节点，包括5位datacenterId和5位workerId<br>
 * 12位序列，毫秒内的计数，12位的计数顺序号支持每个节点每毫秒(同一机器，同一时间截)产生4096个ID序号<br>
 * 加起来刚好64位，为一个Long型。<br>
 * SnowFlake的优点是，整体上按照时间自增排序，并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)，并且效率较高，经测试，SnowFlake每秒能够产生26万ID左右。
 */
public class SnowFlakeGenerator {
	//集群id(0~31)
    private long datacenterId;
    //机器id(0~31)
    private long workerId;
    //序列号
    private long sequenceId;

    //集群id的bit位数
    private long datacenterIdBits = 5L;
    //机器id的bit位数
    private long workerIdBits = 5L;
    //序列号的bit位数(0~4095)
    private long sequenceIdBits = 12L;

    //集群id的最大编号
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    //机器id的最大编号
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    //序列号的掩码
    private long sequenceIdMask = -1L ^ (-1L << sequenceIdBits);

    //生成最终结果时，集群id需移动的bit位数
    private long timestampShiftBits = sequenceIdBits + workerIdBits + datacenterIdBits;
    //生成最终结果时，集群id需移动的bit位数
    private long datacenterIdShiftBits = sequenceIdBits + workerIdBits;
    //生成最终结果时，机器id需移动的bit位数
    private long workerIdShiftBits = sequenceIdBits;

    //去掉过去的时间，即从指定时间(本例以2017-10-12 00:00:00)开始算，
    // 大约可用69.5年（41位的时间位，最大值换成毫秒，再换算成年，大约69.5年）
    //1514736000000为从1970-01-01 00:00:00到2018-01-01 00:00:00经过的毫秒数
    private long pastMills = 1514736000000L;
    //上一次生成id使用的timestamp ,以毫秒为单位
    private long lastTimestamp = -1L;

    
    
  //单台机器固定调用
    static SnowFlakeGenerator sfg=null;
    public static SnowFlakeGenerator getInstance(){
    	if (sfg == null) {
    		sfg=new SnowFlakeGenerator(1,1);
    	}
    	return sfg;
    }
    
    
    /**
     * 若没有指定集群id和机器id,则默认均为0
     */
    public SnowFlakeGenerator() {
        this(0, 0);
    }

    /**
     * 指定集群id和机器id
     *
     * @param datacenterId
     * @param workerId
     */
    public SnowFlakeGenerator(long datacenterId, long workerId) {
        if (datacenterId < 0 || datacenterId > maxDatacenterId) {
            throw new RuntimeException(String.format("datacenterId greater than %d or less than 0", maxDatacenterId));
        }
        if (workerId < 0 || workerId > maxWorkerId) {
            throw new RuntimeException(String.format("workerId greater than %d or less than 0", maxWorkerId));
        }
        this.datacenterId = datacenterId;
        this.workerId = workerId;
    }

    /**
     * 生成全局唯一的id
     *
     * @return
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {  //出现这种情况，通常是由于机器时间出问题了
            throw new RuntimeException("machine time error");
        }

        //同一时刻生成的id号
        if (timestamp == lastTimestamp) {
            sequenceId = (sequenceId + 1) & sequenceIdMask;
            if (sequenceId == 0) {  //说明当前毫秒的序列号用完了，需从下个毫秒数开始重新计数
                timestamp = nextTimestamp(lastTimestamp);
            }
        } else {
            //否则序列号从0开始
            sequenceId = 0L;
        }

        lastTimestamp = timestamp;
        long id = ((timestamp - pastMills) << timestampShiftBits)
                | (datacenterId << datacenterIdShiftBits)
                | (workerId << workerIdShiftBits)
                | sequenceId;
        return id;
    }

    /**
     * 获取上次取数毫秒的下一时刻
     *
     * @param lastTimestamp
     * @return
     */
    long nextTimestamp(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) throws Exception {
    	/*SimpleDateFormat  sdf=new  SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    	Date dt=sdf.parse("2018-01-01 00:00:00");
    	System.out.println(dt.getTime());
    	
    	SnowFlakeGenerator snowFlakeGenerator = new SnowFlakeGenerator();
        //测试，生成10个唯一id
        for (int i = 0; i < 10; i++) {
            long id = snowFlakeGenerator.nextId();
            System.out.println(id);
        }*/
    }
}
